/* Copyright (c) 2010, mitsuhisa.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of the misuhisa.net nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef NYUTREE_HPP_
#define NYUTREE_HPP_

#include "NyuValue.hpp"

class NyuContext;
class NyuString;

class NyuTree {
public:
	virtual ~NyuTree();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false) = 0;

    virtual NyuValue eval_put(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_set(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_iadd(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_isub(NyuValue v2, NyuContext* c, bool is_tail = false);

	//static void* operator new(size_t size);
	//static void operator delete(void* p);
};

class NyuTreeList : public NyuTree {
public:
	NyuTreeList(NyuTree* value, NyuTreeList* next = 0);
	virtual ~NyuTreeList();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

	NyuTreeList* reverse() {
		NyuTreeList* l = this;
		NyuTreeList* m = 0;
		NyuTreeList* n = 0;

		while (l) {
			n = l->next_;
			l->next_ = m;
			m = l;
			l = n;
		}

		return m;
	}

	NyuTree* value() {
		return value_;
	}

	NyuTreeList* next() {
		return next_;
	}

private:
	NyuTree* value_;
	NyuTreeList* next_;
};

class NyuTreeUnit : public NyuTree {
public:
	NyuTreeUnit(NyuTreeList* stmts = 0);
	virtual ~NyuTreeUnit();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTreeList* stmts_;
};

class NyuTreeExpr1 : public NyuTree {
public:
	NyuTreeExpr1(NyuTree* node);
	virtual ~NyuTreeExpr1();

protected:
	NyuTree* node_;
};

#define NYU_TREE_DEFINE_EXPR1(NAME)                             \
class NyuTree##NAME : public NyuTreeExpr1 {                     \
public:                                                         \
	NyuTree##NAME(NyuTree* node) :                              \
		NyuTreeExpr1(node) {}                                   \
	virtual NyuValue eval(NyuContext* c, bool is_tail = false); \
}

NYU_TREE_DEFINE_EXPR1(Not);
NYU_TREE_DEFINE_EXPR1(Invert);
NYU_TREE_DEFINE_EXPR1(Pos);
NYU_TREE_DEFINE_EXPR1(Neg);
NYU_TREE_DEFINE_EXPR1(Incr);
NYU_TREE_DEFINE_EXPR1(Decr);
NYU_TREE_DEFINE_EXPR1(PostIncr);
NYU_TREE_DEFINE_EXPR1(PostDecr);

#undef NYU_TREE_DEFINE_EXPR1

class NyuTreeExpr2 : public NyuTree {
public:
	NyuTreeExpr2(NyuTree* lnode, NyuTree* rnode);
	virtual ~NyuTreeExpr2();

protected:
	NyuTree* lnode_;
	NyuTree* rnode_;
};

#define NYU_TREE_DEFINE_EXPR2(NAME)                             \
class NyuTree##NAME : public NyuTreeExpr2 {                     \
public:                                                         \
	NyuTree##NAME(NyuTree* lnode, NyuTree* rnode) :             \
		NyuTreeExpr2(lnode, rnode) {}                           \
	virtual NyuValue eval(NyuContext* c, bool is_tail = false); \
}

NYU_TREE_DEFINE_EXPR2(Add);
NYU_TREE_DEFINE_EXPR2(Sub);
NYU_TREE_DEFINE_EXPR2(Mul);
NYU_TREE_DEFINE_EXPR2(Div);
NYU_TREE_DEFINE_EXPR2(Mod);
NYU_TREE_DEFINE_EXPR2(Lt);
NYU_TREE_DEFINE_EXPR2(Le);
NYU_TREE_DEFINE_EXPR2(Gt);
NYU_TREE_DEFINE_EXPR2(Ge);
NYU_TREE_DEFINE_EXPR2(LShift);
NYU_TREE_DEFINE_EXPR2(RShift);
NYU_TREE_DEFINE_EXPR2(OwnProto);
NYU_TREE_DEFINE_EXPR2(OwnMember);
NYU_TREE_DEFINE_EXPR2(Cmp);
NYU_TREE_DEFINE_EXPR2(Eq);
NYU_TREE_DEFINE_EXPR2(Ne);
NYU_TREE_DEFINE_EXPR2(And);
NYU_TREE_DEFINE_EXPR2(Xor);
NYU_TREE_DEFINE_EXPR2(Or);
NYU_TREE_DEFINE_EXPR2(LAnd);
NYU_TREE_DEFINE_EXPR2(LXor);
NYU_TREE_DEFINE_EXPR2(LOr);

NYU_TREE_DEFINE_EXPR2(Put);
NYU_TREE_DEFINE_EXPR2(Set);
NYU_TREE_DEFINE_EXPR2(IAdd);
NYU_TREE_DEFINE_EXPR2(ISub);
NYU_TREE_DEFINE_EXPR2(IMul);
NYU_TREE_DEFINE_EXPR2(IDiv);
NYU_TREE_DEFINE_EXPR2(IMod);
NYU_TREE_DEFINE_EXPR2(ILShift);
NYU_TREE_DEFINE_EXPR2(IRShift);
NYU_TREE_DEFINE_EXPR2(IAnd);
NYU_TREE_DEFINE_EXPR2(IXor);
NYU_TREE_DEFINE_EXPR2(IOr);
NYU_TREE_DEFINE_EXPR2(ILAnd);
NYU_TREE_DEFINE_EXPR2(ILOr);

#undef NYU_TREE_DEFINE_EXPR2

class NyuTreeCond : public NyuTree {
public:
	NyuTreeCond(NyuTree* cond, NyuTree* tnode, NyuTree* fnode);
	virtual ~NyuTreeCond();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTree* cond_;
	NyuTree* tnode_;
	NyuTree* fnode_;
};

class NyuTreeInt : public NyuTree {
public:
	NyuTreeInt(NyuInt value);
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuValue value_;
};

class NyuTreeFloat : public NyuTree {
public:
    NyuTreeFloat(NyuFloat value);
    virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
    NyuValue value_;
};

class NyuTreeStr : public NyuTree {
public:
    NyuTreeStr(char* value);
    virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
    NyuValue value_;
};

class NyuTreeSym : public NyuTree {
public:
    NyuTreeSym(char* value);
    virtual NyuValue eval(NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_put(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_set(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_iadd(NyuValue v2, NyuContext* c, bool is_tail = false);
    virtual NyuValue eval_isub(NyuValue v2, NyuContext* c, bool is_tail = false);

private:
    NyuString* symbol_;
    unsigned int symidx_;
};

class NyuTreeBlock : public NyuTree {
public:
	NyuTreeBlock(NyuTreeList* stmts);
	virtual ~NyuTreeBlock();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTreeList* stmts_;
};

class NyuTreeCurblk : public NyuTree {
public:
	NyuTreeCurblk();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);
};

class NyuTreeArglen : public NyuTree {
public:
    NyuTreeArglen();
    virtual NyuValue eval(NyuContext* c, bool is_tail = false);
};

class NyuTreeArgref : public NyuTree {
public:
	NyuTreeArgref(int idx);
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	int idx_;
};

class NyuTreeCall : public NyuTree {
public:
	NyuTreeCall(NyuTree* expr, NyuTreeList* args);
	virtual ~NyuTreeCall();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTree* expr_;
	NyuTreeList* args_;
};

class NyuTreeObject : public NyuTree {
public:
	NyuTreeObject(NyuTreeList* slots);
	virtual ~NyuTreeObject();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTreeList* slots_;
};

class NyuTreeObjectSlot : public NyuTree {
public:
	NyuTreeObjectSlot(char* name, NyuTree* value);
	virtual ~NyuTreeObjectSlot();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

	NyuString* name() {
	    return name_;
	}

	NyuTree* value() {
	    return value_;
	}

private:
	NyuString* name_;
	NyuTree* value_;
};

class NyuTreeArray : public NyuTree {
public:
	NyuTreeArray(NyuTreeList* elms);
	virtual ~NyuTreeArray();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTreeList* elms_;
};

class NyuTreeRange : public NyuTree {
public:
    NyuTreeRange(NyuTree* start, NyuTree* end, NyuTree* step,
                 bool is_include_border);
    virtual ~NyuTreeRange();
    virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
    NyuTree* start_;
    NyuTree* end_;
    NyuTree* step_;
    bool is_include_border_;
};

class NyuTreeSlotAccess : public NyuTree {
public:
	NyuTreeSlotAccess(NyuTree* expr, char* name);
	virtual ~NyuTreeSlotAccess();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);
	virtual NyuValue eval_iadd(NyuValue v2, NyuContext* c, bool is_tail = false);
	virtual NyuValue eval_isub(NyuValue v2, NyuContext* c, bool is_tail = false);

private:
	NyuTree* expr_;
	NyuString* name_;
};

class NyuTreeGetItem : public NyuTree {
public:
	NyuTreeGetItem(NyuTree* expr, NyuTree* idx);
	virtual ~NyuTreeGetItem();
	virtual NyuValue eval(NyuContext* c, bool is_tail = false);

private:
	NyuTree* expr_;
	NyuTree* idx_;
};

#endif /* NYUTREE_HPP_ */
